﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;

namespace BaseDomainNamespace.Builders
{
    /// <summary>
    /// エンティティビルダのエラーを探索するためのクラスです。
    /// </summary>
    /// <remarks>
    /// <para>
    /// 通常、エンティティビルダのエラー通知メソッドを使用するためにはエラーハンドラ実装クラスを定義する必要がありますが、
    /// エラーの有無を判別する目的に限っては、エラーハンドラ実装クラスを定義する必要がありません。
    /// 代わりに、<see cref="ErrorFinder&lt;THandler&gt;"/> クラスを利用します。
    /// このクラスのインスタンスの <see cref="ErrorFinder&lt;THandler&gt;.GetAgent()"/> メソッドで取得したエージェントを引数に指定してエラー通知メソッドを呼び出すと、
    /// エージェントがエラーを見つけたかどうかを <see cref="ErrorFinder&lt;THandler&gt;.Found"/> プロパティから取得することができます。
    /// </para>
    /// </remarks>
    /// <typeparam name="THandler">エラーハンドラインターフェイスの型。</typeparam>
    /// <seealso cref="IEntityBuilder&lt;TEntity&gt;"/>
    internal sealed class ErrorFinder<THandler>
            where THandler : class
    {
        /// <summary>
        /// エラーが見つかったかどうかを示す値を取得します。
        /// </summary>
        public bool Found
        {
            get;
            private set;
        }

        /// <summary>
        /// エラー通知メソッドに渡されることでエラーを探索するエージェントを取得します。
        /// </summary>
        /// <returns>エージェント。</returns>
        public THandler GetAgent()
        {
            var handler = new Action(() => Found = true);
            var agent = new ErrorHandlerProxy(handler);
            return (THandler)agent.GetTransparentProxy();
        }


        /// <summary>
        /// エラーハンドラインターフェイスのプロキシです。
        /// </summary>
        private sealed class ErrorHandlerProxy : RealProxy
        {
            /// <summary>
            /// <see cref="ErrorHandlerProxy"/> クラスの新しいインスタンスを初期化します。
            /// </summary>
            /// <param name="handlerMethod">エラーハンドラメソッドが呼び出された時に実行するメソッド。</param>
            public ErrorHandlerProxy(Action handlerMethod)
                : base(typeof(THandler))
            {
                _handlerMethod = handlerMethod;
            }

            /// <summary>
            /// エラーハンドラメソッドが呼び出された時に実行するメソッド。
            /// </summary>
            private readonly Action _handlerMethod;


            /// <summary>
            /// メソッド呼び出しをフックします。
            /// </summary>
            /// <param name="msg">メソッド呼び出しメッセージ。</param>
            /// <returns>メソッド呼び出しの応答メッセージ。</returns>
            public override IMessage Invoke(IMessage msg)
            {
                var methodCallMessage = (IMethodCallMessage)msg;
                _handlerMethod();
                return new ReturnMessage(null, null, 0, methodCallMessage.LogicalCallContext, methodCallMessage);
            }
        }
    }
}
